import random
from typing import Annotated, Optional

from fastapi import APIRouter, Query, HTTPException
from fastapi_pagination import Page
from fastapi_pagination.ext.tortoise import paginate
from pydantic import UUID4

from base.base_schemas import BaseSelectOptionSchema
from utils.auth import public_route
from .filters import TagFilter, CategoryFilter, ArticleFilter
from .models import Tag, Category, Article, Thought
from .schemas import (
    TagOutSchema,
    TagEditSchema,
    CategoryOutSchema,
    CategoryEditSchema,
    CategoryOutWithoutChildrenSchema,
    ArticleOutSchema,
    ArticleEditSchema,
    ArticleFilterSchema,
    ThoughtOutSchema,
    ThoughtEditSchema,
)
from apps.message.models import Message
from .search import search_service

router = APIRouter()


# Tag routes
@router.get("/tags", response_model=Page[TagOutSchema])
async def get_tag_list(
    name: Optional[str] = Query(None),
    active: Optional[bool] = Query(None),
):
    query = Tag.all()
    query = TagFilter.filter(query, {"name": name, "active": active})
    return await paginate(query)


@router.get("/tags/public", response_model=list[TagOutSchema])
@public_route
async def get_public_tag_list():
    query = Tag.filter(active=True)
    return await query.all()


@router.get("/tags/list_select_options", response_model=list[BaseSelectOptionSchema])
async def get_tag_list_select_options():
    query = Tag.filter()
    data = await query.all()
    return [
        {
            "label": item.name,
            "value": item.id,
            "active": item.active,
        }
        for item in data
    ]


@router.post("/tags", response_model=TagOutSchema)
async def create_tag(data: TagEditSchema):
    obj = await Tag.create(**data.model_dump())
    return obj


@router.put("/tags/{tag_id}", response_model=TagOutSchema)
async def update_tag(tag_id: UUID4, data: TagEditSchema):
    obj = await Tag.get_or_raise(id=tag_id)
    await obj.update_from_dict(data.model_dump(exclude_unset=True))
    await obj.save()
    return obj


@router.delete("/tags/batch_delete")
async def batch_delete_tags(tag_ids: list[UUID4]):
    if await Article.filter(tags__id__in=tag_ids).count():
        raise HTTPException(status_code=400, detail="标签下存在文章，无法删除")
    await Tag.filter(id__in=tag_ids).delete()
    return None


@router.delete("/tags/{tag_id}")
async def delete_tag(tag_id: UUID4):
    obj = await Tag.get_or_raise(id=tag_id)
    if await obj.articles.all().count():
        raise HTTPException(status_code=400, detail="标签下存在文章，无法删除")
    await obj.delete()
    return None


# Category routes
@router.get("/categories", response_model=Page[CategoryOutSchema])
async def get_category_list(
    name: Optional[str] = Query(None),
    active: Optional[bool] = Query(None),
    page: Optional[int] = Query(1),
    size: Optional[int] = Query(10),
):
    query = Category.all()
    query = CategoryFilter.filter(query, {"name": name, "active": active})
    total = await query.count()
    pages = total // size + 1
    paginated_query = await query.limit(size).offset((page - 1) * size)
    category_ids = [category.id for category in paginated_query]
    items = await Category.get_tree(id__in=category_ids)
    return {"total": total, "pages": pages, "size": size, "page": page, "items": items}


@router.get("/categories/public", response_model=list[CategoryOutSchema])
@public_route()
async def get_public_category_list():
    query = Category.filter(active=True)
    return await query.all()


@router.get(
    "/categories/list_select_options",
    response_model=list[BaseSelectOptionSchema],
)
async def get_category_list_select_options():
    query = Category.filter()
    data = await query.all()
    return [
        {
            "label": item.name,
            "value": item.id,
            "active": item.active,
        }
        for item in data
    ]


@router.post("/categories", response_model=CategoryOutWithoutChildrenSchema)
async def create_category(data: CategoryEditSchema):
    obj = await Category.create(**data.model_dump())
    return obj


@router.put(
    "/categories/{category_id}", response_model=CategoryOutWithoutChildrenSchema
)
async def update_category(category_id: UUID4, data: CategoryEditSchema):
    obj = await Category.get_or_raise(id=category_id)
    await obj.update_from_dict(data.model_dump(exclude_unset=True))
    await obj.save()
    return obj


@router.delete("/categories/batch_delete")
async def batch_delete_categories(category_ids: list[UUID4]):
    # 检查是否有文章
    if await Article.filter(category_id__in=category_ids).count():
        raise HTTPException(status_code=400, detail="分类下存在文章，无法删除")
    await Category.filter(id__in=category_ids).delete()
    return None


@router.delete("/categories/{category_id}")
async def delete_category(category_id: UUID4):
    obj = await Category.get_or_raise(id=category_id)
    if await obj.articles.all().count():
        raise HTTPException(status_code=400, detail="分类下存在文章，无法删除")
    await obj.delete()
    return None


# Article routes
@router.get("/articles", response_model=Page[ArticleOutSchema])
async def get_article_list(filter_query: Annotated[ArticleFilterSchema, Query()]):
    query = Article.all().prefetch_related("category", "tags")
    query = ArticleFilter.filter(query, filter_query)

    # 获取分页结果
    page = await paginate(query)

    # 手动处理结果以包含 tag_ids
    results = []
    for article in page.items:
        article_dict = article.dict()
        article_dict["tag_ids"] = [str(tag.id) for tag in article.tags]
        results.append(ArticleOutSchema(**article_dict))

    # 创建新的分页对象，包含处理后的结果
    return Page(items=results, total=page.total, page=page.page, size=page.size)


@router.get("/articles/public", response_model=Page[ArticleOutSchema])
@public_route()
async def get_public_article_list(
    q: Optional[str] = Query(None),
    category: Optional[UUID4] = Query(None),
    tags: Optional[list[UUID4]] = Query(None),
    category_short_name: Optional[str] = Query(None),
):
    filters = {
        "title": q,
        "category": category,
        "tags": tags,
        "category_short_name": category_short_name,
    }
    query = (
        Article.filter(status="p")
        .filter(active=True)
        .prefetch_related("category", "tags")
    )
    query = ArticleFilter.filter(query, filters)
    return await paginate(query)


@router.get("/articles/public/search")
@public_route()
async def search_public_articles(q: str, page: int = 0, size: int = 10):
    results = await search_service.search_articles(q, size, page)
    return results


@router.get("/articles/public/top", response_model=list[ArticleOutSchema])
@public_route()
async def get_public_top_articles():
    query = Article.filter(status="p", active=True, top=True).prefetch_related(
        "category", "tags"
    )
    return await query.all()


@router.get("/articles/public/random", response_model=list[ArticleOutSchema])
@public_route()
async def get_public_random_articles():
    query = Article.filter(status="p", active=True, top=True).prefetch_related(
        "category", "tags"
    )
    query = await query.all()
    random_count = 3 if len(query) >= 3 else len(query)
    return random.sample(query, random_count)


@router.get("/articles/public/{article_id}", response_model=ArticleOutSchema)
@public_route()
async def get_public_article_detail(article_id: UUID4):
    article = await Article.get_or_raise(
        id=article_id, status="p", active=True
    ).prefetch_related("category", "tags")
    article.views += 1
    await article.save()
    article.tag_ids = [str(tag.id) for tag in article.tags]
    return article


@router.get("/articles/{article_id}", response_model=ArticleOutSchema)
async def get_article_detail(article_id: UUID4):
    obj = await Article.get_or_raise(id=article_id).prefetch_related("category", "tags")
    obj.tag_ids = [str(tag.id) for tag in obj.tags]
    return obj


@router.post("/articles", response_model=ArticleOutSchema)
async def create_article(data: ArticleEditSchema):
    obj = await Article.create(
        **data.model_dump(exclude={"tags", "tag_ids"}),
    )
    if data.tag_ids:
        tags = await Tag.filter(id__in=data.tag_ids)
        await obj.tags.add(*tags)
    # 添加到搜索索引
    search_service.add_article(obj)
    article = await Article.get(id=obj.id).prefetch_related("category", "tags")
    article.tag_ids = [str(tag.id) for tag in article.tags]
    return article


@router.put("/articles/{article_id}", response_model=ArticleOutSchema)
async def update_article(article_id: UUID4, data: ArticleEditSchema):
    obj = await Article.get_or_raise(id=article_id)
    await obj.update_from_dict(
        data.model_dump(exclude={"tags", "tag_ids"}, exclude_unset=True)
    )
    await obj.save()
    if data.tag_ids:
        tags = await Tag.filter(id__in=data.tag_ids)
        await obj.tags.clear()
        await obj.tags.add(*tags)
    # 更新搜索索引
    search_service.update_article(obj)
    await obj.save()
    article = await Article.get(id=article_id).prefetch_related("category", "tags")
    article.tag_ids = [str(tag.id) for tag in article.tags]
    return article


@router.delete("/articles/batch_delete")
async def batch_delete_articles(article_ids: list[UUID4]):
    await Article.filter(id__in=article_ids).delete()
    # 从搜索索引中删除
    search_service.delete_article(article_ids)
    return None


@router.delete("/articles/{article_id}")
async def delete_article(article_id: UUID4):
    obj = await Article.get_or_raise(id=article_id)
    await obj.delete()
    # 从搜索索引中删除
    search_service.delete_article(str(article_id))
    return None


@router.get("/thoughts", response_model=Page[ThoughtOutSchema])
async def get_thought_list(
    content: Optional[str] = Query(None),
):
    query = Thought.all().order_by("-created_at")
    if content:
        query = query.filter(content__icontains=content)
    return await paginate(query)


@router.get("/thoughts/public", response_model=Page[ThoughtOutSchema])
@public_route()
async def get_public_thought_list():
    query = Thought.filter(active=True).order_by("-created_at")
    return await paginate(query)


@router.post("/thoughts", response_model=ThoughtOutSchema)
async def create_thought(data: ThoughtEditSchema):
    obj = await Thought.create(**data.model_dump())
    return obj


@router.put("/thoughts/{thought_id}", response_model=ThoughtOutSchema)
async def update_thought(thought_id: UUID4, data: ThoughtEditSchema):
    obj = await Thought.get_or_raise(id=thought_id)
    await obj.update_from_dict(data.model_dump(exclude_unset=True))
    await obj.save()
    return obj


@router.delete("/thoughts/batch_delete")
async def batch_delete_thoughts(thought_ids: list[UUID4]):
    await Thought.filter(id__in=thought_ids).delete()
    return None


@router.delete("/thoughts/{thought_id}")
async def delete_thought(thought_id: UUID4):
    obj = await Thought.get_or_raise(id=thought_id)
    await obj.delete()
    return None


@router.get("/archive")
@public_route()
async def get_archive():
    article_count = await Article.filter(status="p", active=True).count()
    tag_count = await Tag.filter(active=True).count()
    comment_count = await Message.filter(active=True).count()

    # 统计各个分类下的文章并返回{name,value}格式，name为分类名，value为文章数量,value为0的分类不返回
    categories = await Category.filter(active=True).all()
    category_chart = [
        {
            "name": category.name,
            "value": await category.articles.filter(status="p", active=True).count(),
        }
        for category in categories
    ]

    tags = await Tag.filter(active=True).all()
    tag_chart = [
        {
            "name": tag.name,
            "value": await tag.articles.filter(status="p", active=True).count(),
        }
        for tag in tags
    ]
    # 查询文章列表，返回按年分组的文章列表，结构为[{year:2021,articles:[{date,title,id}]}]
    articles = await Article.filter(status="p", active=True).order_by("-created_at")
    article_archive = []
    for article in articles:
        year = article.created_at.year
        if not article_archive or article_archive[-1]["year"] != year:
            article_archive.append({"year": year, "articles": []})
        article_archive[-1]["articles"].append(
            {
                "date": article.created_at.strftime("%m-%d"),
                "title": article.title,
                "id": str(article.id),
            }
        )

    return {
        "overview": {
            "article": article_count,
            "tag": tag_count,
            "comment": comment_count,
        },
        "statistics": {
            "category": category_chart,
            "tag": tag_chart,
        },
        "archive": article_archive,
    }


@router.get("/articles/search")
async def search_articles(q: str, limit: int = 10, offset: int = 0):
    results = await search_service.search_articles(q, limit, offset)
    return results
